/*LICENSE_BEGIN
 * ******************************************************************
 * Copyright 2006 by the Dainamite Robocup-Team at the
 * DAI-Labor, TU-Berlin (www.dai-labor.de)
 * 
 * Members of Dainamite are:
 * Holger Endert
 * Robert Wetzker
 * Thomas Karbe
 * Axel Heßler
 * Philippe Bütter
 * Felix Brossmann
 * 
 * Licensed under the GNU GPL (General Public License) Free Software 
 * License, Version 2. You may use this file only in compliance with 
 * the License. You may obtain a copy of the License at:
 * 
 * http://www.fsf.org/licensing/licenses/gpl.txt
 * ******************************************************************
LICENSE_END*/
package robocup.component.util;

/**
 * Last-in-First-out-List. This is an endless List of fixed length ;-). When the
 * index reaches the end of the array it will return to the beginning. <p/>
 * Example: add(a),add(b),getLast()->b add(a),add(b),getLast(1)->a
 * add(a),add(b),getLast(2)->null getLast(>=length)->null getLast( <0)->null
 * 
 */
public class RingList<T> {

	protected int index = -1;

	/** the number of contained items */
	protected int itemCount = 0;

	/** the "list" */
	private T[] a;

	/**
	 * Constructor a list which is linked to a ring
	 * 
	 * @param length -
	 *            number of items the list contains.
	 */
	@SuppressWarnings("unchecked")
	public RingList(int length) {
		// create array of Type T
		this.a = (T[]) java.lang.reflect.Array
				.newInstance(Object.class, length);
	};

	/**
	 * Removes all items of the list
	 */
	public void clear() {
		for (int i = 0; i < this.a.length; i++) {
			this.a[i] = null;
		}
		this.itemCount = 0;
	}

	/**
	 * The list length.
	 * 
	 * @return int - length of list
	 */
	public int lengthMax() {
		return this.a.length;
	}

	/**
	 * The number of items inserted.
	 * 
	 * @return the number of items inserted (= length of list when list filled)
	 */
	public int length() {
		return this.itemCount;
	}

	/**
	 * Gets the item inserted on the specified position before the last item.
	 * throws IndexOutOfBoundsException when list is empty, when called with a
	 * neg. value or when the parameter is greater than the list's length. <p/>
	 * Examples: <p/> getLast(0) -> getLast() -> last inserted item getLast(1) ->
	 * the item inserted before the last item etc.
	 * 
	 * @param beforeLast
	 * @return the item inserted on the specified position before the last item.
	 */
	public T getLast(int beforeLast) {
		if (this.index == -1 || beforeLast > this.a.length - 1
				|| beforeLast < 0)
			throw new IndexOutOfBoundsException();
		beforeLast = this.index - beforeLast;
		beforeLast = (beforeLast < 0) ? beforeLast + this.a.length : beforeLast;
		return (T) this.a[beforeLast];
	}

	/**
	 * Gets the last inserted item. throws IndexOutOfBoundsException when list
	 * is empty.
	 * 
	 * @return last inserted item
	 */
	public T getLast() {
		return (T) getLast(0);
	}

	/**
	 * Inserts an item. If the list was full before, the oldest item is removed.
	 * 
	 * @param val -
	 */
	public void insert(T val) {
		this.index++;
		if (this.index >= this.a.length) {
			this.index = 0;
		}
		this.a[this.index] = val;
		if (this.itemCount < this.a.length) {
			this.itemCount++;
		}

	}

	/**
	 * Tells if the list is empty
	 * 
	 * @return true when empty
	 */
	public boolean isEmpty() {
		return (this.itemCount == 0);
	}

	/**
	 * gets the minimum value of a RingList<Long>
	 * 
	 * @param maxItems
	 *            only the last 'maxItems' are considered
	 * @return
	 */
	public Long getMin(int maxItems) {
		if (this.length() == 0)
			return null;

		if (!(this.getLast() instanceof Long)) {
			return null;
		}

		long min = Long.MAX_VALUE;
		for (int i = 0; i < Math.min(this.length(), maxItems); i++) {
			if ((Long) this.getLast(i) < min)
				min = (Long) this.getLast(i);
		}
		return min;
	}

	/**
	 * gets the minimum value of a RingList<Long>
	 * 
	 * @param maxItems
	 *            only the last 'maxItems' are considered
	 * @return
	 */
	public Long getMax(int maxItems) {
		if (this.length() == 0)
			return null;

		if (!(this.getLast() instanceof Long)) {
			return null;
		}

		long max = Long.MIN_VALUE;
		for (int i = 0; i < Math.min(this.length(), maxItems); i++) {
			if ((Long) this.getLast(i) > max)
				max = (Long) this.getLast(i);
		}
		return max;
	}
}
